/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.config.spring.beans.factory.annotation;

import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;

import com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationAttributes;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
import static com.alibaba.spring.util.AnnotationUtils.getAttributes;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.dubbo.config.spring.beans.factory.annotation.ServiceBeanNameBuilder.create;
import static org.springframework.util.StringUtils.hasText;

/**
 * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
 * that Consumer service {@link Reference} annotated fields
 *
 * @since 2.5.7
 */
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
        ApplicationContextAware, ApplicationListener<ServiceBeanExportedEvent> {

    /**
     * The bean name of {@links ReferenceAnnotationBeanPostProcessor}
     */
    public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor";

    /**
     * Cache size
     */
    private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32);

    private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
            new ConcurrentHashMap<>(CACHE_SIZE);

    private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedFieldReferenceBeanCache =
            new ConcurrentHashMap<>(CACHE_SIZE);

    private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedMethodReferenceBeanCache =
            new ConcurrentHashMap<>(CACHE_SIZE);

    private final ConcurrentMap<String, ReferencedBeanInvocationHandler> referencedBeanInvocationHandlersCache =
            new ConcurrentHashMap<>();

    private ApplicationContext applicationContext;

    /**
     * To support the legacy annotation that is @com.alibaba.dubbo.config.annotation.Reference since 2.7.3
     */
    public ReferenceAnnotationBeanPostProcessor() {
        /**
         *
         * 处理@Reference注入点，就需要是每个bean调用后置处理器的时候需要判断那些注解要经过这个后置处理器进行处理
         * 所以这个后置处理器是只处理@Reference的，所以这里调用父类的构造，将我这个后置处理器要处理的注解传过去
         * 这里为了兼容阿里之前的@Reference注解，所以传入来了两个注解
         */
        super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
    }

    /**
     * Gets all beans of {@link ReferenceBean}
     *
     * @return non-null read-only {@link Collection}
     * @since 2.5.9
     */
    public Collection<ReferenceBean<?>> getReferenceBeans() {
        return referenceBeanCache.values();
    }

    /**
     * Get {@link ReferenceBean} {@link Map} in injected field.
     *
     * @return non-null {@link Map}
     * @since 2.5.11
     */
    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedFieldReferenceBeanMap() {
        return Collections.unmodifiableMap(injectedFieldReferenceBeanCache);
    }

    /**
     * Get {@link ReferenceBean} {@link Map} in injected method.
     *
     * @return non-null {@link Map}
     * @since 2.5.11
     */
    public Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> getInjectedMethodReferenceBeanMap() {
        return Collections.unmodifiableMap(injectedMethodReferenceBeanCache);
    }

    /**
     * ReferenceAnnotationBeanPostProcessor是一个bea的后置处理器,主要来处理@Reference注解的，我们想一下，服务提供者和
     * 服务消费者，服务提供者主要是在类上加@Service注解来标识自己是一个dubbo服务，而服务消费者一般是在属性上或者方法上加上这个主注解
     * 来代表是一个服务消费者，所以服务消费者一般是在service类的属性上加的注解，那么处理方式就不太一样的，也就是说必须在这个service被
     * 实例化过后来进行依赖注入，所以@Reference注解是在属性注入阶段，也就是bean实例化进行的操作，所以需要bean的后置才处理器，而不是bean工厂的
     * 后置处理器
     *
     * 而下面的这个方法其实是bean的后置处理器在调用后置处理方法，比如属性注入的时候去寻找注入点，找到注入点过后
     * 也就是找到了加了@Reference的属性或者方法的时候，需要进行注入的时候过来调用的这个方法，也就是说doGetInjectedBean
     * 是父类来调用的，它只是一个子类中的方法，后置处理器的调用是在父类中完成的，子类提供了获取注入点对象的方法doGetInjectedBean
     * 调用顺序：
     * AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
     *  》AbstractAnnotationBeanPostProcessor#findInjectionMetadata
     * 》AbstractAnnotationBeanPostProcessor#buildAnnotatedMetadata
     *  》AbstractAnnotationBeanPostProcessor#findFieldAnnotationMetadata
     *  》AbstractAnnotationBeanPostProcessor#findAnnotatedMethodMetadata
     *   》AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement#inject
     *   》AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement#inject
     *    》AbstractAnnotationBeanPostProcessor#getInjectedObject
     *     》ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
     * @param attributes
     * @param bean
     * @param beanName
     * @param injectedType
     * @param injectedElement
     * @return
     * @throws Exception
     */

    @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
        /**
         * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
         * 这里得到的是一个bean的名字，这个bean的名字就是ServiceBean的名字，这里是根据
         * @Reference注解信息生成的一个bean的名字，和处理@Servie的时候生成的ServiceBean的名字是一样的
         * 因为@Reference注解和@Service注解属性是差不多一模一样的，所以它里面也有version和group，说白了@Reference是引用了服务提供者通过@Service提供的对象
         * 所以他们两个注解的只是代表的目的不同，但是注解的信息相同，@Service是代表提供者，@Reference是代表消费者
         * 所以这里referencedBeanName就是ServiceBean的名字，这里为什么要获取到呢？其实dubbo的意思很简单，就是说如果说这个@Reference所要注入的对象是
         * 从注册中心所来的，那么本地的spring容器肯定没有这个对象，所以它需要将这个ServieBean（注册中心提供）放入到本地的spring容器中
         * 所以这里要生成要给ServiceBean的名字：ServiceBean:com.xxx.Interface:1.0.0:xxx
         */
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

        /**
         * The name of bean that is declared by {@link Reference @Reference} annotation injection
         * 这里生成的是@Refreence的一个beanName，被@Refreence注解的属性或者方法需要的注入对象可能来自于本地
         * 也可能来自于注册中心，所以这里会为真正的注入对象生成一个bean的名字，最后创建一个代理对象
         * 放入spring容器，bean的名字生成规则是@Refreence的信息，包含属性信息，比如
         * @Reference(version="1.0.0.1",group="default",protocol="p1")
         * 所以bean的名字就是就是根据@Refreence注解来生成的，注解有多少属性就连接起来作为bean的名字
         * 这里大概分析下下面几行代码的逻辑：
         * 1.构建一个新的ReferenceBean,这个ReferenceBean和@Service的ServiceBean的意思差不多，就是说加了@Reference的属性
         * 或者方法dubbo需要生成一个代理对象，真正的调用的时候是通过代理对象去调用的，但是我们知道远程调用有很多的参数
         * 比如协议，远程调用的地址，注册中心的地址这些信息，如果单单是一个代理对象是无法保存信息的，所以需要用一个对象ReferenceBean
         * 来保存这些信息，这个和@Servie的ServiceBean是一个概念；
         * 1.生成一个@Reference注解的名字,被这个注解加注的属性或者方法可能是来自于远程服务提供者，
         * 如果是远程的，那么注册一个bean，注册的bean的名字就是根据@Reference生成的名字，注册的ban对象就是刚刚构建出来的
         * ReferenceBean对象，如果这个bean是本地的bean，那么注解向容器中注册一个别名，别名也是根据@Reference生成的名字，
         * 就是说被@Reference加注的属性或者方法不管注入的对象是来自于远程还是本地，那么在spring容器中都会有一个bean
         * 叫做ReferenceBean，bean的名字是根据@Reference生成的一个名字
         */
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);

        /**
         * 构建一个新的ReferenceBean，这个ReferenceBean包含了很多信息，和@Service一样，比如version，group，protocol等
         * 信息都有，因为作为消费端去调用，我们得知道要调用那个接口的版本，以什么协议调用，所以创建了一个ReferenceBean来保存这些信息
         * 而ReferenceBean中有一个ref的属性，这个属性就是一个代理对象，这个代理对象就是具体要调用的逻辑都在这个代理对象中
         * ReferenceBean还是一个FactoryBean，所以真正的注入的时候注入的对象是ReferenceBean.getObject得到的对象
         */
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

        //判断是否是一个本地bean还是远程bean
        boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);

        /**
         * 注册bean，这里的是直接注册单例的bean，不是BeanDefinition，这里的注册是这样的
         *   1.如果这个baen来自于本地，那么注册一个别名：先通过 referencedBeanName（ServieBean:xxx:xxx)取到这个bean，然后注册一个别名，别名就是@Reference(...)
         *   2.如果不是本地的，那么直接向容器中注册一个单例bean，bean的名字是@Reference(...),bean是ReferenceBean
         *
         *   简单来说就是bean是本地的：
         *    ServiceBean:xxx:xxx
         *     @Reference(...)
         *    都对应了一个ServiceBean
         *   如果说远程的：
         *   @Reference(...)对应了远程的一个bean叫ReferenceBean
         *   那么注册到了spring容器有什么用呢？如果通过@Reference引入过后，后面的bean就可以直接使用
         * @AutoWired来进行注入了，我们都知道@AutoWried是在bean的最后一个阶段进行注入的，所以当一个Service
         * 中的属性或者方法加了@Reference的话，那么其他的bean也可以使用@AutoWired来引入，为什么要这么做？因为
         * @Reference的信息实在太多了，每个都加的话，有点繁琐，可能dubbo的是这样考虑的，但是如果一个@Reference通过
         * @AutoWried注入的话，那么可能也会引起歧义，这是第一个普通的本地注入还是远程的？所以只是dubbo提供了这种功能
         * 而已，具体要怎么使用就看开发者了
         *
         */
        registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);

        //处理注入点缓存的
        cacheInjectedReferenceBean(referenceBean, injectedElement);

        /**
         * 这里是得到ReferenceBean的代理对象的，这里其实很简单，因为referenceBean是一个FactoryBean，在FactoryBean中
         * 有一个getObject（）方法得到真正的对象，而getObject方法恰恰返回的就是一个referenceBean的代理对象，在代理对象中
         * 封装了很多的信息，当通过@Reference加注的属性或者方法被注入成功以后，其实注入的是referenceBean的getObject
         * 返回的一个代理对象，在referenceBean中有一个属性T ref都是代理对象，getObject返回的就是这个ref，所以referenceBean
         * 是一个FactoryBean，还是一个包含ref代理对象的一个bean
         * dubbo在这个版本返回的代理对象有
         * JdkProxyFactory
         * JavassistProxyFactory
         * jdk的和javassist的字节码的代理对象，其实最终处理的handler都是
         * InvokerInvocationHandler中的invoke方法
         */
        return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
    }

    /**
     * Register an instance of {@link ReferenceBean} as a Spring Bean
     *
     * @param referencedBeanName The name of bean that annotated Dubbo's {@link Service @Service} in the Spring {@link ApplicationContext}
     * @param referenceBean      the instance of {@link ReferenceBean} is about to register into the Spring {@link ApplicationContext}
     * @param attributes         the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
     * @param localServiceBean   Is Local Service bean or not
     * @param interfaceClass     the {@link Class class} of Service interface
     * @since 2.7.3
     */
    private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean,
                                       AnnotationAttributes attributes,
                                       boolean localServiceBean, Class<?> interfaceClass) {

        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        String beanName = getReferenceBeanName(attributes, interfaceClass);

        if (localServiceBean) {  // If @Service bean is local one
            /**
             * Get  the @Service's BeanDefinition from {@link BeanFactory}
             * Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
             */
            AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
            RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref");
            // The name of bean annotated @Service
            String serviceBeanName = runtimeBeanReference.getBeanName();
            // register Alias rather than a new bean name, in order to reduce duplicated beans
            beanFactory.registerAlias(serviceBeanName, beanName);
        } else { // Remote @Service Bean
            if (!beanFactory.containsBean(beanName)) {
                beanFactory.registerSingleton(beanName, referenceBean);
            }
        }
    }

    /**
     * Get the bean name of {@link ReferenceBean} if {@link Reference#id() id attribute} is present,
     * or {@link #generateReferenceBeanName(AnnotationAttributes, Class) generate}.
     *
     * @param attributes     the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
     * @param interfaceClass the {@link Class class} of Service interface
     * @return non-null
     * @since 2.7.3
     */
    private String getReferenceBeanName(AnnotationAttributes attributes, Class<?> interfaceClass) {
        // id attribute appears since 2.7.3
        String beanName = getAttribute(attributes, "id");
        if (!hasText(beanName)) {
            beanName = generateReferenceBeanName(attributes, interfaceClass);
        }
        return beanName;
    }

    /**
     * Build the bean name of {@link ReferenceBean}
     *
     * @param attributes     the {@link AnnotationAttributes attributes} of {@link Reference @Reference}
     * @param interfaceClass the {@link Class class} of Service interface
     * @return
     * @since 2.7.3
     */
    private String generateReferenceBeanName(AnnotationAttributes attributes, Class<?> interfaceClass) {
        StringBuilder beanNameBuilder = new StringBuilder("@Reference");

        if (!attributes.isEmpty()) {
            beanNameBuilder.append('(');
            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
                beanNameBuilder.append(entry.getKey())
                        .append('=')
                        .append(entry.getValue())
                        .append(',');
            }
            // replace the latest "," to be ")"
            beanNameBuilder.setCharAt(beanNameBuilder.lastIndexOf(","), ')');
        }

        beanNameBuilder.append(" ").append(interfaceClass.getName());

        return beanNameBuilder.toString();
    }

    /**
     * Is Local Service bean or not?
     *
     * @param referencedBeanName the bean name to the referenced bean
     * @return If the target referenced bean is existed, return <code>true</code>, or <code>false</code>
     * @since 2.7.6
     */
    private boolean isLocalServiceBean(String referencedBeanName, ReferenceBean referenceBean, AnnotationAttributes attributes) {
        return existsServiceBean(referencedBeanName) && !isRemoteReferenceBean(referenceBean, attributes);
    }

    /**
     * Check the {@link ServiceBean} is exited or not
     *
     * @param referencedBeanName the bean name to the referenced bean
     * @return if exists, return <code>true</code>, or <code>false</code>
     * @revised 2.7.6
     */
    private boolean existsServiceBean(String referencedBeanName) {
        return applicationContext.containsBean(referencedBeanName) &&
                applicationContext.isTypeMatch(referencedBeanName, ServiceBean.class);

    }

    private boolean isRemoteReferenceBean(ReferenceBean referenceBean, AnnotationAttributes attributes) {
        boolean remote = Boolean.FALSE.equals(referenceBean.isInjvm()) || Boolean.FALSE.equals(attributes.get("injvm"));
        return remote;
    }

    /**
     * Get or Create a proxy of {@link ReferenceBean} for the specified the type of Dubbo service interface
     *
     * @param referencedBeanName   The name of bean that annotated Dubbo's {@link Service @Service} in the Spring {@link ApplicationContext}
     * @param referenceBean        the instance of {@link ReferenceBean}
     * @param localServiceBean     Is Local Service bean or not
     * @param serviceInterfaceType the type of Dubbo service interface
     * @return non-null
     * @since 2.7.4
     * 不管是本地bean还是远程的，其实    最终都是获取的 referenceBean中的ref代理对象
     */
    private Object getOrCreateProxy(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean,
                                    Class<?> serviceInterfaceType) {
        if (localServiceBean) { // If the local @Service Bean exists, build a proxy of Service
            return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
                    newReferencedBeanInvocationHandler(referencedBeanName));
        } else {
            exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately
            return referenceBean.get();
        }
    }


    private void exportServiceBeanIfNecessary(String referencedBeanName) {
        if (existsServiceBean(referencedBeanName)) {
            ServiceBean serviceBean = getServiceBean(referencedBeanName);
            if (!serviceBean.isExported()) {
                serviceBean.export();
            }
        }
    }

    private ServiceBean getServiceBean(String referencedBeanName) {
        return applicationContext.getBean(referencedBeanName, ServiceBean.class);
    }

    private InvocationHandler newReferencedBeanInvocationHandler(String referencedBeanName) {
        return referencedBeanInvocationHandlersCache.computeIfAbsent(referencedBeanName,
                ReferencedBeanInvocationHandler::new);
    }

    /**
     * The {@link InvocationHandler} class for the referenced Bean
     */
    @Override
    public void onApplicationEvent(ServiceBeanExportedEvent event) {
        initReferencedBeanInvocationHandler(event.getServiceBean());
    }

    private void initReferencedBeanInvocationHandler(ServiceBean serviceBean) {
        String serviceBeanName = serviceBean.getBeanName();
        referencedBeanInvocationHandlersCache.computeIfPresent(serviceBeanName, (name, handler) -> {
            handler.init();
            return null;
        });
    }

    private class ReferencedBeanInvocationHandler implements InvocationHandler {

        private final String referencedBeanName;

        private Object bean;

        private ReferencedBeanInvocationHandler(String referencedBeanName) {
            this.referencedBeanName = referencedBeanName;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                if (bean == null) {
                    init();
                }
                result = method.invoke(bean, args);
            } catch (InvocationTargetException e) {
                // re-throws the actual Exception.
                throw e.getTargetException();
            }
            return result;
        }

        private void init() {
            ServiceBean serviceBean = applicationContext.getBean(referencedBeanName, ServiceBean.class);
            this.bean = serviceBean.getRef();
        }
    }

    @Override
    protected String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
                                                 Class<?> injectedType, InjectionMetadata.InjectedElement injectedElement) {
        return buildReferencedBeanName(attributes, injectedType) +
                "#source=" + (injectedElement.getMember()) +
                "#attributes=" + getAttributes(attributes, getEnvironment());
    }

    /**
     * @param attributes           the attributes of {@link Reference @Reference}
     * @param serviceInterfaceType the type of Dubbo's service interface
     * @return The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
     */
    private String buildReferencedBeanName(AnnotationAttributes attributes, Class<?> serviceInterfaceType) {
        ServiceBeanNameBuilder serviceBeanNameBuilder = create(attributes, serviceInterfaceType, getEnvironment());
        return serviceBeanNameBuilder.build();
    }

    private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
                                                     Class<?> referencedType)
            throws Exception {

        //缓存
        ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);

        if (referenceBean == null) {
            ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                    .create(attributes, applicationContext)
                    .interfaceClass(referencedType);
            //构建一个新的ReferenceBean对象，将之前启动的时候在配置中所拿到的一些配置对象取出来赋值给ReferenceBean对象
            //比如在配置解析的时候dubbo.application对应的是ApplicationConfig，这个对象是封装了参数配置的信息的，所以会放入
            //spring容器中的，类似这样的赋值过程还包括了注册中心的配置信息、方法的配置信息、接口的配置信息、监控中心的配置信息
            //反正就是一大堆的配置对象都拿出来赋值给ReferenceBean，和@Service的处理是一样的
            referenceBean = beanBuilder.build();
            referenceBeanCache.put(referenceBeanName, referenceBean);
        } else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
            throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
                    referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
        }
        return referenceBean;
    }

    private void cacheInjectedReferenceBean(ReferenceBean referenceBean,
                                            InjectionMetadata.InjectedElement injectedElement) {
        if (injectedElement.getMember() instanceof Field) {
            injectedFieldReferenceBeanCache.put(injectedElement, referenceBean);
        } else if (injectedElement.getMember() instanceof Method) {
            injectedMethodReferenceBeanCache.put(injectedElement, referenceBean);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void destroy() throws Exception {
        super.destroy();
        this.referenceBeanCache.clear();
        this.referencedBeanInvocationHandlersCache.clear();
        this.injectedFieldReferenceBeanCache.clear();
        this.injectedMethodReferenceBeanCache.clear();
    }
}
